package org.wheel.plugins.toolkit.sql.capability;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.wheel.plugins.toolkit.sql.core.AbstractWrapper;
import org.wheel.plugins.toolkit.sql.enums.SqlFunction;
import org.wheel.plugins.toolkit.sql.function.FunctionBuilder;
import org.wheel.plugins.toolkit.sql.helper.LambdaUtils;
import org.wheel.plugins.toolkit.sql.helper.SFunction;

/**
 * 聚合/函数 select 能力
 * @param <W> 子类型
 * @param <T> 主实体
 */
public interface LambdaAggregate<W extends LambdaAggregate<W,T>, T> {
    /* ---------- 快捷聚合 ---------- */
    default <J> W count(SFunction<J,?> col, String columnAlias){
        return function(SqlFunction.COUNT.name(), col, columnAlias);
    }
    default W count(String columnAlias){
        return function(SqlFunction.COUNT.name(), (SFunction<?,?>)null, (String)columnAlias);
    }
    default W count(){
        return function(SqlFunction.COUNT.name(), (SFunction<?,?>)null, "COUNT");
    }
    default <J> W count(SFunction<J, ?> col){
        return count(col, null);
    }
    default <J>  W sum(SFunction<J,?> col, String columnAlias){
        return function(SqlFunction.SUM.name(), col, columnAlias);
    }
    default <J>  W sum(SFunction<J,?> col){
        String fieldName = LambdaUtils.getFieldName(col);
        return sum(col, fieldName);
    }
    default <J>  W sum(FunctionBuilder col, SFunction<J,?> columnAlias){
        return function(SqlFunction.SUM.name(), col, columnAlias);
    }
    default <J>  W sum(FunctionBuilder col, String columnAlias){
        return function(SqlFunction.SUM.name(), col, columnAlias);
    }
    default <J>  W avg(SFunction<J,?> col, String columnAlias){
        return function(SqlFunction.AVG.name(), col, columnAlias);
    }
    default <J>  W avg(SFunction<J,?> col){
        String fieldName = LambdaUtils.getFieldName(col);
        return avg(col, fieldName);
    }
    default <J>  W avg(FunctionBuilder col, SFunction<J,?> columnAlias){
        return function(SqlFunction.AVG.name(), col, columnAlias);
    }
    default <J>  W avg(FunctionBuilder col, String columnAlias){
        return function(SqlFunction.AVG.name(), col, columnAlias);
    }
    default <J>  W max(SFunction<J,?> col){
        String fieldName = LambdaUtils.getFieldName(col);
        return function(SqlFunction.MAX.name(), col, fieldName);
    }
    default <J>  W max(SFunction<J,?> col, String columnAlias){
        return function(SqlFunction.MAX.name(), col, columnAlias);
    }
    default <J, R>  W max(SFunction<J,?> col, SFunction<R, ?> columnAlias){
        String colAlias = null;
        if(ObjectUtil.isNotNull(columnAlias)){
            colAlias = LambdaUtils.getFieldName(col);
        }
        return function(SqlFunction.MAX.name(), col, colAlias);
    }
    default <J>  W max(FunctionBuilder col, String columnAlias){
        return function(SqlFunction.MAX.name(), col, columnAlias);
    }
    default <J>  W max(FunctionBuilder col, SFunction<J, ?> columnAlias){
        return function(SqlFunction.MAX.name(), col, columnAlias);
    }
    default <J>  W min(SFunction<J,?> col, String columnAlias){
        return function(SqlFunction.MIN.name(), col, columnAlias);
    }
    default <J>  W min(SFunction<J,?> col){
        String fieldName = LambdaUtils.getFieldName(col);
        return min(col, fieldName);
    }
    default <J>  W min(FunctionBuilder col, String columnAlias){
        return function(SqlFunction.MIN.name(), col, columnAlias);
    }
    default <J>  W min(FunctionBuilder col, SFunction<J, ?> columnAlias){
        return function(SqlFunction.MIN.name(), col, columnAlias);
    }

    /* ---------- 通用函数 ---------- */
    default W function(String funcName, FunctionBuilder fnc){
        FunctionBuilder fn1 = FunctionBuilder.fn(funcName)
                .col(fnc.bind((AbstractWrapper<?, ?>) this).toSegment())
                .bind((AbstractWrapper<?, ?>) this);
        return function(fn1);
    }
    default <J> W function(String funcName, FunctionBuilder fnc, SFunction<J,?> columnAlias){
        FunctionBuilder fn1 = FunctionBuilder.fn(funcName)
                .col(fnc.bind((AbstractWrapper<?, ?>) this).toSegment())
                .bind((AbstractWrapper<?, ?>) this).as(columnAlias);
        return function(fn1);
    }
    default W function(String funcName, FunctionBuilder fnc, String columnAlias){
        FunctionBuilder fn1 = FunctionBuilder.fn(funcName)
                .col(fnc.bind((AbstractWrapper<?, ?>) this).toSegment())
                .bind((AbstractWrapper<?, ?>) this).as(columnAlias);
        return function(fn1);
    }

    default <J> W function(String funcName, SFunction<J,?> col, String columnAlias){
        if(StrUtil.isBlank(columnAlias)){
            columnAlias = LambdaUtils.getFieldName(col);
        }
        FunctionBuilder fn = FunctionBuilder.fn(funcName)
                    .col(col)
                    .as(columnAlias)
                    .bind((AbstractWrapper<?, ?>) this);
        if(col == null){
            fn.col("*");
        }
        if(col != null) {
            fn.setTableEntityClass(LambdaUtils.getCapturingClass(col));
        }
        return function(fn);   // 延迟注入别名
    }

    /* ---------- 原子方法 ---------- */
    W function(FunctionBuilder fn);
}